package de.ruedigermoeller.fastcast.service.fcdemon;

import de.ruedigermoeller.fastcast.remoting.FCFutureResultHandler;
import de.ruedigermoeller.fastcast.remoting.FCRemoting;
import de.ruedigermoeller.fastcast.remoting.FCSendContext;
import de.ruedigermoeller.fastcast.remoting.FastCast;
import de.ruedigermoeller.fastcast.util.FCLog;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 * User: ruedi
 * Date: 9/14/13
 * Time: 10:39 PM
 * To change this template use File | Settings | File Templates.
 */
public abstract class FCStartScript {
    protected FCRemoting rem;
    protected FCDemon demon;
    protected String fchome;
    protected List errors = new ArrayList();
    protected List<FCDemon.NodeInfo> receivers;
    protected int recProcCount[]; // count process started on receivers

    public void setFchome(String fchome) {
        this.fchome = fchome;
    }

    public FCRemoting getRemoting() {
        return rem;
    }

    public FCDemon getDemon() {
        return demon;
    }

    public String getFchome() {
        return fchome;
    }

    public void initCluster() throws IOException {
        rem = FastCast.getRemoting();
        demon = (FCDemon) rem.getRemoteService("fcdemon");
    }

    public List getErrors() {
        return errors;
    }

    public abstract void startCluster(String arg, List<FCDemon.NodeInfo> demons);


    /**
     * starts a java process on a random node
     * @param c
     * @param arg
     */
    public FCDemon.ProcessEntry startNode( final Class c, String arg ) {
        return startNode(c.getSimpleName(), "./lib/*:./shared/lib/*", "", c, arg );
    }

    /**
     * starts a java process on a random node
     * @param c
     * @param arg
     */
    public FCDemon.ProcessEntry startNode( String vmOpt, final Class c, String arg ) {
        return startNode(c.getSimpleName(), "./lib/*:./shared/lib/*", vmOpt, c, arg );
    }

    /**
     * starts a java process on a random node
     * @param c
     * @param arg
     */
    public FCDemon.ProcessEntry startNode( final String tag, String cp, String vmoptions, final Class c, String arg ) {
        String receiver = getMinLoadProcess();
        if ( receiver == null ) {
            System.out.println("no process host demon found");
            return null;
        }
        FCSendContext.get().setReceiver(receiver);
        final CountDownLatch count = new CountDownLatch(1);
        final FCDemon.ProcessEntry res[] = { null };
        demon.startJavaClass(
                tag,
                vmoptions,
                c.getName(),
                arg,
                cp,
                new FCFutureResultHandler() {

                    @Override
                    public void timeoutReached() {
                        String err = "timeout reached while trying to start node " + c.getName() + " " + tag;
                        errors.add(err);
                    }

                    @Override
                    public void resultReceived(Object obj, String sender) {
                        if (obj instanceof FCDemon.ProcessEntry == false) {
                            errors.add("" + obj);
                        } else {
                            done();
                            res[0] = (FCDemon.ProcessEntry) obj;
                        }
                        count.countDown();
                    }
                });
        try {
            count.await(5000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
        }
        if ( count.getCount() != 0 ) {
            errors.add("could not start process "+c.getName());
        }
        return res[0];
    }

    protected void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            FCLog.log(e);
        }
    }

    /**
     * start a java process on node denoted by nodeId (as obtained by demon node list)
     * @param nodeId
     * @param tag
     * @param c
     * @param arg
     */
    public FCDemon.ProcessEntry startNode( String nodeId, String tag, String cp, String vmoptions, Class c, String arg ) {
        FCSendContext.get().setReceiver(nodeId);
        return startNode(tag, cp, vmoptions, c, arg);
    }

    public String getMinLoadProcess() {
        int minIdx = 0;
        int minC = 99999990;
        for (int i = 0; i < recProcCount.length; i++) {
            int i1 = recProcCount[i];
            if ( i1 < minC ) {
                minIdx = i;
                minC = i1;
            }
        }
        if ( minIdx < receivers.size() )
        {
            recProcCount[minIdx]++;
            return receivers.get(minIdx).getNodeId();
        }
        return null;
    }

    public void setReceivers(List<FCDemon.NodeInfo> receivers) {
        this.receivers = receivers;
        recProcCount = new int[receivers.size()];
        for (int i = 0; i < receivers.size(); i++) {
            FCDemon.NodeInfo info = receivers.get(i);
            recProcCount[i] = info.getProcesses().size();
        }
    }

    public List<FCDemon.NodeInfo> getReceivers() {
        return receivers;
    }
}
